/* 
 * File:   disk.cpp
 * Author: Chen Liu     
 * Student number: 3996591
 * 
 * Created on October 22, 2012, 2:19 AM
 */

#include "disk.h"
#include <vector>
#include <cmath>
#include <string>
#include <iostream>

using namespace std;

DiskScheduler::DiskScheduler(int NumTracks, int HdSpeed, int *RefString, int N) {

    numt = NumTracks;
    numrs = N;
    hspeed = HdSpeed;
    refs = new int[numrs];
    point = new int[numrs];
    reqt = new double[numrs];
    
    for (int i = 0; i < numrs; i++) {
        refs[i] = RefString[i];
    }
}

double DiskScheduler::AccessDisk(AlgType Alg, int TrackRequestsPerSec) {
    reqpsec = TrackRequestsPerSec;
    
    switch(Alg){
        case eFCFS:
            return FCFS();
        case eSSTF:
            return SSTF();
        case eSCAN:
            return SCAN();
        case eCSCAN:
            return CSCAN();
        case eLOOK:
            return LOOK();
        case eCLOOK:
            return CLOOK();                   
    }
    return 0;
}

int DiskScheduler::GetReq(int *reqlist, double time) {
    int length = 0;

    for (int i = 0; i < numrs; i++) {
        if (reqt[i] > time)
            break;
        else {
            if (point[i] == 0) {
                reqlist[length] = i;
                length++;
            }
        }
    }
    return length;
}

void DiskScheduler::Sort(int *reqlist, int length) {
    int m, n;
    int temp;

    for (int i = 0; i < length; i++) {
        for (int j = i + 1; j < length; j++) {
            m = reqlist[i];
            n = reqlist[j];
            if (refs[m] > refs[n]) {
                temp = m;
                reqlist[i] = n;
                reqlist[j] = temp;
            }
        }
    }
}

int DiskScheduler::GetMin(int *reqlist, int length) {
    if (length > 0)
        return reqlist[0];
    else
        return -1;
}

int DiskScheduler::GetNext(int *reqlist, int length, int current) {
    bool bFound;
    int req;

    bFound = false;
    for (int i = 0; i < length; i++) {
        req = reqlist[i];
        if (refs[req] >= current) {
            bFound = true;
            break;
        }
    }
    if (bFound)
        return req;
    else
        return -1;
}

int DiskScheduler::GetPre(int *reqlist, int length, int current) {
    int req;
    bool bFound;

    bFound = false;
    for (int i = length - 1; i >= 0; i--) {
        req = reqlist[i];
        if (refs[req] < current) {
            bFound = true;
            break;
        }
    }
    if (bFound)
        return req;
    else
        return -1;
}

double DiskScheduler::FCFS() {
    int current = 0;
    int total = 0;
    double time = 0;

    for (int i = 0; i < numrs; i++) {
        if (i == 0)
            current = abs(refs[i]);
        else
            current = abs(refs[i] - refs[i - 1]);
        total = total + current;
    }
    time = (double)total / hspeed;
    return time;
}

double DiskScheduler::SSTF() {
    int min = 0;
    int start = 0;
    double time = 0;
    double totaltime = 0;
    int *reqlist;
    bool flag = true;
    int length = 1;
    int req = 0;


    for (int i = 0; i < numrs; i++) {
        reqt[i] = (1000 / reqpsec) * i;
        point[i] = 0;
    }

    reqlist = new int[numrs];
    reqlist[0] = 0;

    while (1) {
        flag = true;
        for (int i=0; i < numrs; i++) {
            if (point[i] == 0) 
            {
                flag = false;
                break;
            }
        }
        
        if (flag == true)
            break;

        if (length > 0) 
        {
            min = numt;
            int temp = 0;
            for (int i = 0; i < length; i++) {
                req = reqlist[i];
                if (!(min < abs(refs[req] - start))) {
                    min = abs(refs[req] - start);
                    temp = req;
                }
            }
            point[temp] = 1;
            start = refs[temp];
            time = time + (double)min / hspeed;
            totaltime = totaltime + (double)min / hspeed;
        }
        length = GetReq(reqlist, totaltime);

        if (length == 0)
            totaltime = totaltime + 1000 / reqpsec;
    }
    return time;
}

double DiskScheduler::SCAN() {
    
    double time;
    double totaltime;
    int *reqlist;
    int start;
    int length;
    int req;
    bool flag;
    bool finish;

    for (int i = 0; i < numrs; i++) {
        reqt[i] = (1000 / reqpsec) * i;
        point[i] = 0;
    }

    reqlist = new int[numrs];
    start = 0; 
    time = 0;
    totaltime = 0;
    reqlist[0] = 0;
    length = 1;
    flag = false; 
    while (1) {
        finish = true;
        for (int i = 0; i < numrs; i++) {
            if (point[i] == 0) 
            {
                finish = false;
                break;
            }
        }
        
        if (finish)
            break;

        if (length > 0) 
        {
            if (flag == false) {
                Sort(reqlist, length);
                req = GetNext(reqlist, length, start);
                if (req >= 0) {
                    time = time + (double) abs(refs[req] - start) / hspeed;
                    totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                    start = refs[req];
                    point[req] = 1;
                    flag = false;
                } else {
                    time = time + (double) abs(numt - start - 1) / hspeed;
                    totaltime = totaltime + (double) abs(numt - start - 1) / hspeed;
                    start = numt - 1;
                    flag = 1;
                }
            } else if (flag == true) {
                Sort(reqlist, length);
                req = GetPre(reqlist, length, start);
                if (req >= 0) {
                    time = time + (double) abs(refs[req] - start) / hspeed;
                    totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                    start = refs[req];
                    point[req] = 1;
                    flag = true;
                } else {
                    time = time + (double) start / hspeed;
                    totaltime = totaltime + (double) start / hspeed;
                    start = 0;
                    flag = false;
                }
            }
        }
        length = GetReq(reqlist, totaltime);

        if (length == 0)
            totaltime = totaltime + 1000 / reqpsec;
    }

    return time;
}

double DiskScheduler::CSCAN() {
    double time;
    double totaltime;
    int *reqlist;
    int start;
    int length;
    int req;
    bool finish;

    for (int i = 0; i < numrs; i++) {
        reqt[i] = (1000 / reqpsec) * i;
        point[i] = 0;
    }

    reqlist = new int[numrs];
    start = 0; 
    time = 0;
    totaltime = 0;
    reqlist[0] = 0;
    length = 1;
    
    while (1) {
        finish = true;
        for (int i = 0; i < numrs; i++) {
            if (point[i] == 0) 
            {
                finish = false;
                break;
            }
        }
        if (finish)
            break;

        if (length > 0) 
        {
            Sort(reqlist, length);
            req = GetNext(reqlist, length, start);
            if (req >= 0) {
                time = time + (double) abs(refs[req] - start) / hspeed;
                totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                start = refs[req];
                point[req] = 1;
            } else {
                time = time + (double) abs(numt - start - 1) / hspeed;
                totaltime = totaltime + (double) abs(numt - start - 1) / hspeed;
                time = time + (double) (numt - 1) / hspeed / 10;
                start = 0;
            }
        }
        length = GetReq(reqlist, totaltime);
        
        if (length == 0)
            totaltime = totaltime + 1000 / reqpsec;
    }

    return time;
}

double DiskScheduler::LOOK() {
    double time;
    double totaltime;
    int *reqlist;
    int start;
    int length;
    int req;
    bool finish;
    bool flag;

    for (int i = 0; i < numrs; i++) {
        reqt[i] = (1000 / reqpsec) * i;
        point[i] = 0;
    }

    reqlist = new int[numrs];
    start = 0; 
    time = 0;
    totaltime = 0;
    reqlist[0] = 0;
    length = 1;
    flag = false; 
    while (true) {
        finish = true;
        for (int i = 0; i < numrs; i++) {
            if (point[i] == 0) // exist not visit
            {
                finish = false;
                break;
            }
        }
        if (finish)
            break;

        if (length > 0) 
        {
            Sort(reqlist, length);
            if (flag == false) {
                req = GetNext(reqlist, length, start);
                if (req >= 0) {
                    time = time + (double) abs(refs[req] - start) / hspeed;
                    totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                    start = refs[req];
                    point[req] = 1;
                    flag = false;
                } else
                    flag = true;
            } else if (flag == true) {
                req = GetPre(reqlist, length, start);
                if (req >= 0) {
                    time = time + (double) abs(refs[req] - start) / hspeed;
                    totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                    start = refs[req];
                    point[req] = 1;
                    flag = true;
                } else
                    flag = false;
            }
        }
        length = GetReq(reqlist, totaltime);

        if (length == 0)
            totaltime = totaltime + 1000 / reqpsec;
    }

    return time;
}

double DiskScheduler::CLOOK() {
    double time;
    double totaltime;
    int *reqlist;
    int start;
    int length;
    int req;
    bool finish;

    for (int i = 0; i < numrs; i++) {
        reqt[i] = (1000 / reqpsec) * i;
        point[i] = 0;
    }

    reqlist = new int[numrs];

    start = 0; 
    time = 0;
    totaltime = 0;
    reqlist[0] = 0;
    length = 1;
    
    while (1) {
        finish = true;
        for (int i = 0; i < numrs; i++) {
            if (point[i] == 0) 
            {
                finish = false;
                break;
            }
        }
        if (finish)
            break;

        if (length > 0) {
            Sort(reqlist, length);
            req = GetNext(reqlist, length, start);
            if (req >= 0) {
                time = time + (double) abs(refs[req] - start) / hspeed;
                totaltime = totaltime + (double) abs(refs[req] - start) / hspeed;
                start = refs[req];
                point[req] = 1;
            } else {

                req = GetMin(reqlist, length);
                time = time + (double) abs(refs[req] - start) / hspeed / 10;
                totaltime = totaltime + (double) abs(refs[req] - start) / hspeed / 10;
                start = refs[req];
                point[req] = 1;
            }
        }
        length = GetReq(reqlist, totaltime);

        if (length == 0)
            totaltime = totaltime + (double) 1000 / reqpsec;
    }

    return time;
}

